package kevin.framework.basic.controller;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.SimpleDateFormatSerializer;

import kevin.framework.basic.common.annotation.ApiDescript;
import kevin.framework.basic.common.annotation.ApiParams;
import kevin.framework.basic.common.annotation.NewOldCompare;
import kevin.framework.basic.common.thread.OperatorDataHolder;
import kevin.framework.basic.common.tree.IListToTree;
import kevin.framework.basic.common.tree.ListToTree;
import kevin.framework.basic.common.ui.AdminControllerHelper;
import kevin.framework.basic.common.ui.AjaxResult;
import kevin.framework.basic.common.ui.ClientButton;
import kevin.framework.basic.common.ui.DataGrid;
import kevin.framework.basic.common.ui.DataGridHelper;
import kevin.framework.basic.common.ui.LanguageMap;
import kevin.framework.basic.common.ui.LanguageStore;
import kevin.framework.basic.common.user.ILoginUserContext;
import kevin.framework.basic.common.user.LoginUser;
import kevin.framework.basic.common.user.UserFunc;
import kevin.framework.basic.common.utils.ClearTempFileTimer;
import kevin.framework.basic.common.utils.DateUtils;
import kevin.framework.basic.common.utils.DownLoadFile;
import kevin.framework.basic.common.utils.DynamicParameter;
import kevin.framework.basic.common.utils.IControlCommon;
import kevin.framework.basic.common.utils.IServiceValid;
import kevin.framework.basic.common.utils.PagingResult;
import kevin.framework.basic.common.utils.SpringContextUtil;
import kevin.framework.basic.common.utils.SysConfigParams;
import kevin.framework.basic.common.utils.UrlInfo;
import kevin.framework.basic.common.xls.CellStyleCache;
import kevin.framework.basic.common.xls.IXlsOperator;
import kevin.framework.basic.common.xls.XlsOperator;
import kevin.framework.basic.service.IBaseService;

/**
 * controller 基类封装
 * 
 * @author kevin.huang
 * @date 2019/01/18
 */
public abstract class ResetfulBaseController<T extends Serializable, K extends Serializable>
    implements IControlCommon<T>, IServiceValid {

    // public final static String pageRoot = "/WEB-INF/views/";
    public final Logger logger = org.slf4j.LoggerFactory.getLogger(getClass());
    protected IBaseService<T, K> service;
    private Method getIdMethod = null;
    protected Class<T> entityClass;
    protected String entityPath;
    protected Class<K> idClass;
    protected static String dateTimeFormat = "yyyy-MM-dd HH:mm";
    protected static int timeoutCode = 11111;
    protected static String FinishKey = "k_finish_down_key_";
    private static int commitNum = 2500;
    protected SerializeConfig dateTimeSerialize = new SerializeConfig();
    protected String controllerSimpleName;

    private Map<String, LanguageMap> languageMap = LanguageStore.getLanguageStore();

    /**
     * 登陆用户的上下文
     ***/
    protected ILoginUserContext loginUserCtx;

    @Resource(name = "loginUserContext")
    public void setLoginUserCtx(ILoginUserContext loginUserCtx) {
        this.loginUserCtx = loginUserCtx;

    }

    /**
     * <p>
     * 构造器
     * </p>
     * <p>
     * 设置实体、id的class
     * </p>
     */
    @SuppressWarnings("unchecked")
    public ResetfulBaseController() {
        controllerSimpleName = this.getClass().getSimpleName();
        Type type = getClass().getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType)type;
            this.entityClass = (Class<T>)parameterizedType.getActualTypeArguments()[0];
            this.idClass = (Class<K>)parameterizedType.getActualTypeArguments()[1];
            String simpleName = this.entityClass.getSimpleName();
            entityPath = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1) + "/";
        }
        dateTimeSerialize.put(Date.class, new SimpleDateFormatSerializer(dateTimeFormat));
    }

    /**
     * 必须在子类中实现，并通过spring注入对应的service给this.service
     ***/
    public abstract void setService(IBaseService<T, K> service);

    /**
     * 用于页面请求，{pageName}为模块路径下的页面名称
     * 
     * @param pageName
     *            页面名称
     * @param request
     *            请求对象
     * @param response
     *            返回对象
     * @throws Exception
     */
    @RequestMapping(value = "/page/{pageName}", method = RequestMethod.GET)
    public String page(@PathVariable("pageName") String pageName, HttpServletRequest request) throws Exception {
        String pageModelPath = setPageModelPath(request);
        String pagePath = pageName;
        if (!"".equals(pageModelPath)) {
            pagePath = pageModelPath + pageName;
        }
        DataGrid dg = getDataGridCfg(request);
        if (dg == null) {
            request.setAttribute("dataGridJson", "{}");
        } else {
            try {
                JSONObject dgJson = JSONObject.parseObject(dg.toJson(request, (IControlCommon)this));
                request.setAttribute("dataGridJson", dgJson);
            } catch (Throwable e) {
                logger.error("{}", e);
            }
        }
        setUpdateObjectJson(request);
        onRequestPage(pageName, request);
        return pagePath;
    }

    /**
     * @Title: onRequestPage @Description: 请求页面 return前 @param pageName @param request @throws Exception @throws
     */
    protected void onRequestPage(String pageName, HttpServletRequest request) throws Exception {

    }

    /**
     * @Title: add @Description: AJAX新增处理 @param request @param response 返回ajax结果 @throws
     */
    @ApiDescript("新增")
    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public @ResponseBody AjaxResult add(HttpServletRequest request) throws Exception {
        T t = AdminControllerHelper.getObjectFromRequest(getLoginUser(), request, entityClass, idClass, false);
        String filterResult = saveFilter(t, false, request);
        if ("".equals(filterResult)) {
            try {
                service.insert(t);
                OperatorDataHolder.setDataObject(t);
                return returnSuccData(getLang("common_saveSucess"), t);
            } catch (Exception ex) {
                logger.error("{}", ex);
                return returnFailData(getLang("common_saveFail"));
            }
        } else {
            return returnFailData(filterResult);
        }
    }

    /**
     * @Title: update @Description: ajax更新 @param request @return 返回ajax结果 @throws Exception @throws
     */
    @ApiDescript("更新")
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public @ResponseBody AjaxResult update(HttpServletRequest request) throws Exception {
        T t = AdminControllerHelper.getObjectFromRequest(getLoginUser(), request, entityClass, idClass, false);
        String filterResult = saveFilter(t, true, request);
        if ("".equals(filterResult)) {
            try {
                service.update(t);
                OperatorDataHolder.setDataObject(t);
                return returnSuccData(getLang("common_updateSucess"), t);
            } catch (Exception ex) {
                logger.error("{}", ex);
                return returnFailData(getLang("common_updateFail"));
            }
        } else {
            return returnFailData(filterResult);
        }
    }

    /**
     * @Title: delete
     * @Description: AJAX删除处理 idList=id / idList=id1,id3,id3
     * @param request
     * @param response
     *            返回ajax结果
     * @throws @method
     */
    @ApiDescript("删除")
    @ApiParams("{'idList':'id1,id2'}")
    @RequestMapping(value = "/delete")
    public @ResponseBody AjaxResult delete(HttpServletRequest request) throws Exception {
        String idList = request.getParameter("idList");
        List<K> pks = new ArrayList<K>();
        // 批量删除
        if (idList.contains(",")) {
            String[] idTmpArr = idList.split(",");
            for (String id : idTmpArr) {
                K k = (K)ConvertUtils.convert(id, idClass);
                pks.add(k);
            }
        } else {// 单条数据删除
            K k = (K)ConvertUtils.convert(idList, idClass);
            pks.add(k);
        }
        String chkRes = checkContraints(pks);
        if (chkRes != null) { // 检测数据存在外部引用
            return returnFailData(chkRes);
        }
        try {
            /*** 删除前读取数据 ****/
            if (pks.size() > 0) {
                List<T> deleteData = getListBeforeDelete(pks);
                if (deleteData.size() > 0) {
                    OperatorDataHolder.setDataObject(deleteData);
                }
                service.deleteBatch(pks);
            }
            return returnSuccData(getLang("common_deleteSucess"), "");
        } catch (Exception ex) {
            logger.error("{}", ex);
            String messege = getLang("common_deleteFail");
            Throwable able = ex.getCause();
            if (able != null) {
                messege = able.getMessage();
                if (messege != null && messege.contains("ORA-02292")) {
                    messege = getLang("common_deleteRefFail");
                }
            }
            return returnFailData(messege);
        }
    }

    /**
     * 删除前，获取将要删除的数据 List<K> pks ：id集合
     **/
    protected List<T> getListBeforeDelete(List<K> pks) throws Exception {
        List<T> list = new ArrayList<T>();
        for (K id : pks) {
            T obj = service.get(id);
            list.add(obj);
        }
        return list;
    }

    /**
     * @Title: list
     * @Description: AJAX查询datagrid数据，支持分页、多条件、排序查询
     * @param request
     * @param response
     * @throws @method
     *             0010106
     */
    @ApiDescript("分页查询")
    @ApiParams("{'page':1,'pageSize':15,'_col_sort_fieldName':'asc'}")
    @RequestMapping(value = "/list")
    public @ResponseBody AjaxResult list(HttpServletRequest request) throws Exception {
        Map<String, Object> queryMap = this.getAllParamMapFromQuery(request, true);
        try {
            int page = Integer.parseInt(String.valueOf(request.getParameter("page")));
            int pageSize = Integer.parseInt(String.valueOf(request.getParameter("pageSize")));
            List<ClientButton> listBtns = new ArrayList<ClientButton>();
            DataGrid gridCfg = getDataGridCfg(request);
            if (gridCfg != null) {
                listBtns = gridCfg.getLineButton();
            }
            DynamicParameter dyParams = new DynamicParameter();
            dyParams.setPage(page);
            dyParams.setSize(pageSize);
            Map<String, Object> userParma = setListParams(request);
            Map<String, String> orderMap = setOrderBy(request);

            if (orderMap != null) {
                if (userParma.containsKey("orderColumn")) {
                    Set<Entry<String, String>> keySet = ((Map<String, String>)userParma.get("orderColumn")).entrySet();
                    for (Entry<String, String> entry : keySet) {
                        String key = entry.getKey();
                        if (!orderMap.containsKey(key)) {
                            orderMap.put(key, entry.getValue());
                        }
                    }
                }
                dyParams.setOrderMap(orderMap);
                userParma.remove("orderColumn");
            } else if (userParma.containsKey("orderColumn")) {
                dyParams.setOrderMap((Map<String, String>)userParma.get("orderColumn"));
                userParma.remove("orderColumn");
            }
            for (Entry<String, Object> entry : queryMap.entrySet()) {
                if (!userParma.containsKey(entry.getKey())) {
                    userParma.put(entry.getKey(), entry.getValue());
                }
            }
            dyParams.setParams(userParma);
            PagingResult<T> pgResult = service.selectPagination(dyParams);
            beforeListReturn(pgResult);
            JSONObject tmpObj = pagingResultToJson(pgResult, request, listBtns);
            // pgResult.toJSONObject(request, entityClass,setExcludeFields(request), this,
            // listBtns);
            setGridHeader(gridCfg, tmpObj, request);
            String json = JSONObject.toJSONString(tmpObj, setJsonDateSerialize(),
                SerializerFeature.DisableCircularReferenceDetect);
            Object res = JSON.parse(json);
            return returnSuccData(getLang("common_querySucess"), res, false);
        } catch (Exception e) {
            logger.error("list data fail: {}", e);
            throw e;
        }
    }

    /***
     * 分页结果转json @Description: TODO @param pgResult 分页结果 @param request 请求对象 @param listBtns 按钮列表 @return
     * 返回ajax结果 @throws IllegalAccessException @throws InvocationTargetException @throws Exception @throws
     */
    protected JSONObject pagingResultToJson(PagingResult<T> pgResult, HttpServletRequest request,
        List<ClientButton> listBtns) throws IllegalAccessException, InvocationTargetException, Exception {
        JSONObject tmpObj = pgResult.toJSONObject(request, entityClass, setExcludeFields(request), this, listBtns);
        return tmpObj;
    }

    /**
     * Restful API 根据id查询返回数据
     * 
     * @param id
     *            数据id
     * @return
     * @throws Exception
     * @author jun.liang
     */
    @ApiDescript("查询")
    @ApiParams("{'id':''}")
    @RequestMapping(value = "/get")
    public @ResponseBody AjaxResult get(String id) throws Exception {
        K k = (K)ConvertUtils.convert(id, idClass);
        T obj = service.get(k);
        return returnSuccData("", obj);
    }

    /**
     * @Title: query @Description: AJAX查询数据，与JSON格式返回 @param request @param response @throws
     */
    @ApiDescript("普通查询")
    @ApiParams("{}")
    @RequestMapping(value = "/query")
    public @ResponseBody AjaxResult query(HttpServletRequest request) throws Exception {
        Map<String, Object> paramMap = setQueryParams(request);
        List<T> list = service.selectParam(paramMap);
        return returnSuccData("", list);
    }

    protected Map<String, MultipartFile> getMultipartFile(HttpServletRequest request) {
        Map<String, MultipartFile> fileMap = new HashMap<String, MultipartFile>();
        if (ServletFileUpload.isMultipartContent(request)) {// 判断表单中是否存在文件
            MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)request;
            fileMap = multipartRequest.getFileMap();
        }
        return fileMap;
    }

    /**
     * @Title: upload
     * @Description: 附件上传
     * @param request
     * @param response
     * @method 0010107
     */
    @ApiDescript("上传")
    @ApiParams("{}")
    @RequestMapping("/upload")
    public void upload(HttpServletRequest request, HttpServletResponse response) throws Exception {
        AjaxResult result = new AjaxResult(0, "");
        if (ServletFileUpload.isMultipartContent(request)) {// 判断表单中是否存在文件
            MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)request;
            Map<String, MultipartFile> fileMap = this.getMultipartFile(request);
            Map<String, String> jsonResultData = new HashMap<String, String>();
            boolean ok = saveFiles(multipartRequest, fileMap, jsonResultData);
            JSONObject jobj = new JSONObject();
            for (Entry<String, String> entry : jsonResultData.entrySet()) {
                jobj.put(entry.getKey(), entry.getValue());
            }
            result.data = jobj;
            if (ok) {
                result.code = 0;
            } else {
                result.code = 1;
                if (logger.isDebugEnabled()) {
                    logger.debug("upload file fail");
                }
            }
        } else {
            result.code = 1;
            if (logger.isDebugEnabled()) {
                logger.debug("upload no file is found");
            }
        }
        response(response, result);
    }

    /**
     * @Title: response @Description: 将Ajax结果利用response返回 @param response @param result @throws IOException @throws
     */
    protected void response(HttpServletResponse response, AjaxResult result) throws IOException {
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/html");
        OutputStream out = response.getOutputStream();
        out.write(JSONObject.toJSONString(result).getBytes("utf-8"));
    }

    /**
     * @Title: saveFiles @Description: 子类重写实现文件保存即可 @param request 请求对象 @param fileMap 文件集合 @param uploadResult
     *         处理的信息结果 @return @throws
     */
    public boolean saveFiles(MultipartHttpServletRequest request, Map<String, MultipartFile> fileMap,
        Map<String, String> uploadResult) {
        for (Entry<String, MultipartFile> entry : fileMap.entrySet()) {
            uploadResult.put(entry.getKey(), entry.getValue().getName());
        }
        return true;
    }

    /**
     * @Title: putTmpFileToDelete @Description: 将需要删除的文件全路径方式到定时任务里面，定时任务会执行删除临时文件 @param path @throws
     */
    public void putTmpFileToDelete(String path) {
        ClearTempFileTimer.putFilePath(path);
    }

    /**
     * 获取应用物理根目录
     **/
    public String getContextDirPath(HttpServletRequest request) {
        return request.getServletContext().getRealPath("/");
    }

    /**
     * @Title: 实现文件下载
     * @Description:
     * @param request
     * @param response
     * @throws Throwable
     */
    @ApiDescript("下载")
    @ApiParams("{}")
    @SuppressWarnings("unchecked")
    @RequestMapping("load")
    public void load(HttpServletRequest request, HttpServletResponse response) throws Exception {
        DownLoadFile downLoadFile = setloadFileInputStream(request);
        if (downLoadFile != null) {
            InputStream inputStream = downLoadFile.getInputStream();
            if (inputStream != null) {
                try {
                    response.reset();
                    response.setCharacterEncoding("utf-8");
                    String downFilename = new String(setDownLoadName(request).getBytes("gb2312"), "iso-8859-1");
                    // response.setHeader("Location",downFilename);
                    response.setHeader("Content-Disposition", "attachment; filename=" + downFilename);
                    ServletOutputStream outputStream = response.getOutputStream();
                    byte[] buffer = new byte[inputStream.available()];
                    int i = -1;
                    while ((i = inputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, i);
                    }
                    // response.flushBuffer();
                    outputStream.flush();
                    afterLoadResponsed(downLoadFile);
                } catch (IOException e) {
                    logger.error("{}", e);
                    // returnFailData("文件下载失败！",response,"text/html");
                    AjaxResult result = new AjaxResult(1, "文件下载失败！");
                    response(response, result);
                }
            }
        } else {
            AjaxResult result = new AjaxResult(1, "文件下载失败！");
            response(response, result);
        }
    }

    /**
     * 下载完成后处理流
     ***/
    protected void afterLoadResponsed(DownLoadFile downLoadFile) {

    }

    /**
     * @Title: setDownLoadName @Description: 设置浏览器下载提示保存的文件名称 @param request @return @throws
     */
    protected String setDownLoadName(HttpServletRequest request) throws Exception {
        return "下载附件";
    }

    /**
     * @Title: setloadFileInputStream @Description: 设置需要下载的文件输入流 @param request @return @throws
     */
    protected DownLoadFile setloadFileInputStream(HttpServletRequest request) {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * @Title: export xls导出 支持按模板导出或者常规的列表导出（支持复杂多表头）
     * @Description: xls导出
     * @param request
     * @param response
     * @throws Throwable
     */
    @ApiDescript("导出xls")
    @ApiParams("{}")
    @SuppressWarnings("unchecked")
    @RequestMapping("exportXls")
    public void exportXls(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (this instanceof IXlsOperator) {
            String finish_down_key = request.getParameter(FinishKey);
            response.setCharacterEncoding("utf-8");
            @SuppressWarnings("rawtypes")
            IXlsOperator xlsOpertor = (IXlsOperator)this;
            Map<String, Object> paramMap = setXlsExpQueryParams(request);
            String sheetName = xlsOpertor.setExportSheetName(request);
            String xlsName = xlsOpertor.setExportXlsFileName(request)
                + DateUtils.formatDateTime(new Date(), "-yyyyMMddHHmmss") + ".xls";
            String isModel = request.getParameter("ismodel");
            CellStyleCache cCache = new CellStyleCache();
            try {
                response.reset(); // 清空输出流
                response.setCharacterEncoding("utf-8");
                response.setHeader("Content-disposition",
                    "attachment; filename=" + new String(xlsName.getBytes("gb2312"), "iso-8859-1")); // 设定输出文件头
                response.setContentType("application/msexcel"); // 定义输出类型
                List<T> reusult = service.selectParam(paramMap);
                ServletOutputStream out = response.getOutputStream();
                XlsOperator xlsOpr = new XlsOperator(cCache);
                if ("1".equals(isModel)) {// 按模板导出
                    FileInputStream modelStream = setXlsModelFileInputStream(request);
                    int startRow = Integer.parseInt(request.getParameter("startrow"));
                    xlsOpr.outXlsByModel(out, modelStream, sheetName, startRow, reusult, xlsOpertor);
                } else {// 常规导出
                    xlsOpr.outNewComplexXls(out, xlsOpertor.setExportSheetTitle(request), sheetName, reusult,
                        xlsOpertor);
                }
                out.flush();
                response.flushBuffer();
                out.close();
                loginUserCtx.setKeyValue(finish_down_key, getLang("common_xlsexport_succ"));
            } catch (Throwable ex) {
                try {
                    loginUserCtx.setKeyValue(finish_down_key, getLang("common_xlsexport_fail"));
                } catch (Exception e) {
                    logger.error("{}", e);
                }
                logger.error("{}", ex);
                AjaxResult result = new AjaxResult(1, getLang("common_xlsexport_fail"));
                response(response, result);
            }
        } else {
            AjaxResult result = new AjaxResult(1, "please impl XlsOperationInterface");
            response(response, result);
        }
    }

    /**
     * 按模板导出时候，设置xls模板的输入流
     ***/
    protected FileInputStream setXlsModelFileInputStream(HttpServletRequest request) throws Exception {
        return null;
    }

    /**
     * 导出xls时候设置导出查询的参数 默认返回空
     ***/
    public Map<String, Object> setXlsExpQueryParams(HttpServletRequest request) throws Exception {
        return null;
    }

    /***
     * 设置XlsConvertResult中用到的参考数据，如用于检测是否存在的数据字典
     ***/
    protected Map<Object, Object> setXlsConvertExtData(HttpServletRequest request) {
        return null;
    }

    /***
     * 这个检测文件生成情况的请求不用拦截
     ***/
    @RequestMapping("checkFileCreate")
    public @ResponseBody AjaxResult checkFileCreate(HttpServletRequest request, HttpServletResponse response)
        throws Throwable {
        String finish_down_key = request.getParameter(FinishKey);
        Object flag = loginUserCtx.getKeyValue(finish_down_key);
        if (flag != null) {
            loginUserCtx.deleteKeyValue(finish_down_key);
        }
        String res = String.valueOf(flag);
        return returnSuccData(res);
    }

    /**
     * @Title: importXls
     * @Description: xls导入
     * @param request
     * @param response
     * @throws Throwable
     */
    @ApiDescript("导入xls")
    @ApiParams("{}")
    @SuppressWarnings("unchecked")
    @RequestMapping("importXls")
    public void importXls(HttpServletRequest request, HttpServletResponse response) throws Exception {

        AjaxResult ajaxRes = new AjaxResult(0, "");

        // 实现了xls操作接口的控制器才具备xls导入功能
        if (this instanceof IXlsOperator) {
            if (ServletFileUpload.isMultipartContent(request)) {// 判断表单中是否存在文件
                long start = 0;
                if (logger.isDebugEnabled()) {
                    start = System.currentTimeMillis();
                }
                @SuppressWarnings("rawtypes")
                IXlsOperator xlsOpertor = (IXlsOperator)this;
                MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)request;
                Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
                Map<Integer, String[]> errorData = new HashMap<Integer, String[]>();
                String sheetName = xlsOpertor.getImportSheetName(multipartRequest);
                int startRow = xlsOpertor.getImportStartRow(multipartRequest);
                Map<String, String[]> reqParams = multipartRequest.getParameterMap();
                Map<Object, Object> extDataMap = setXlsConvertExtData(request);
                int result = 0;
                try {
                    MultipartFile file = getImportXlsfile(fileMap);
                    if (file != null && file.getSize() > 0) {
                        InputStream inStream = file.getInputStream();
                        List<T> list = XlsOperator.inputXls(inStream, sheetName, startRow, xlsOpertor, errorData,
                            reqParams, extDataMap);
                        result = service.xlsBatchInsert(list, commitNum);
                        String errData = "";
                        if (errorData.size() > 0) {
                            errData = this.getLang("common_xlsErrRow");
                            Set<Integer> rows = errorData.keySet();
                            for (int r : rows) {
                                errData = errData + " " + String.valueOf(r);
                            }
                        }
                        String m = String.valueOf(((double)System.currentTimeMillis() - (double)start) / 1000);
                        String importRes = this.getLang("common_xlsImportRes");
                        String errCount = String.valueOf(errorData.size() + list.size() - result);
                        String msg = importRes.replaceFirst("_count_", String.valueOf(result))
                            .replaceFirst("_err_count_", errCount).replaceFirst("_time_", m);
                        ajaxRes.code = 0;
                        ajaxRes.message = msg + ";" + errData;
                        ajaxRes.data = "";
                    } else {
                        ajaxRes.code = 1;
                        ajaxRes.message = this.getLang("common_requestNotFile");
                        ajaxRes.data = "";
                    }
                } catch (Throwable ex) {
                    logger.error("{}", ex);
                    ajaxRes.code = 1;
                    ajaxRes.message = this.getLang("common_xlsImportException");
                    ajaxRes.data = "";
                }
            } else {
                ajaxRes.code = 1;
                ajaxRes.message = this.getLang("common_requestNotFile");
                ajaxRes.data = "";
            }
        } else {
            ajaxRes.code = 1;
            ajaxRes.message = "please impl XlsOperationInterface！";
            ajaxRes.data = "";
        }
        response(response, ajaxRes);
    }

    /**
     * @Title: 从请求的fileMap总获取要导入的xls文件 @Description: TODO @param fileMap @return @throws
     */
    protected MultipartFile getImportXlsfile(Map<String, MultipartFile> fileMap) {
        return null;
    }

    /**
     * @Title: setQueryParams 用于子类重写返回query查询的参数 ，默认无参数，返回null @param request @return @throws
     */
    public Map<String, Object> setQueryParams(HttpServletRequest request) {

        return null;
    }

    /***
     * 不需要转为json的字段集合
     ***/
    public Set<String> setExcludeFields(HttpServletRequest request) throws Exception {
        return null;
    }

    /***
     * 设置排序字段，如 orderMap.put("createTime","asc") 支持多字段排队 _sort$_fieldName
     ***/
    public Map<String, String> setOrderBy(HttpServletRequest request) {
        Map<String, String> pre = new HashMap<String, String>();
        Map<String, String[]> prMap = request.getParameterMap();
        for (Entry<String, String[]> entry : prMap.entrySet()) {
            String key = entry.getKey();
            if (key.contains("_col_sort_")) {
                pre.put(key.replace("_col_sort_", ""), entry.getValue()[0]);
            }
        }
        return pre;
    }

    /***
     * 设置分页查询参数
     ***/
    protected Map<String, Object> setListParams(HttpServletRequest request) throws Exception {
        return new HashMap<String, Object>();
    }

    /***
     * 设置datagrid的表头，一般用于实现动态表头
     ****/
    protected void setGridHeader(DataGrid gridCfg, JSONObject tmpObj, HttpServletRequest request) {

    }

    /**
     * @Title: beforeListReturn @Description: 返回前的重写处理 @param pgResult @throws
     */
    protected void beforeListReturn(PagingResult<T> pgResult) {

    }

    /**
     * 从request中提取除分页参数外的所有参数【_sort$_、gridid、pageSize、page、_diff_除外】
     * 
     * @throws UnsupportedEncodingException
     **/
    public Map<String, Object> getAllParamMapFromQuery(HttpServletRequest request, boolean urlDecode)
        throws UnsupportedEncodingException {
        Map<String, Object> map = new HashMap<String, Object>();
        Map<String, String[]> prMap = request.getParameterMap();
        for (Entry<String, String[]> entry : prMap.entrySet()) {
            String key = entry.getKey();
            if (key.contains("_sort$_") || key.equals("_diff_") || "gridid".equals(key) || "pageSize".equals(key)
                || "page".equals(key)) {
                continue;
            }
            String[] values = entry.getValue();
            if (values.length > 0) {
                String v = values[0];
                if (StringUtils.isNotEmpty(v)) {
                    if (urlDecode) {
                        map.put(key, URLDecoder.decode(v, "utf-8"));
                    } else {
                        map.put(key, v);
                    }
                }
            }
        }
        return map;
    }

    /**
     * @Title: checkContraints @Description: 检查数据约束情况 @param pks id列表 @return @throws Exception @throws
     */
    protected String checkContraints(List<K> pks) throws Exception {
        Map<String, Object> list = new HashMap<String, Object>();
        list.put("keys", pks);
        /*
         * List<String> langKeys = this.service.checkContraints(list);
         * if(langKeys.size()>0){ //检测数据存在外部引用 StringBuffer msg=new StringBuffer();
         * for(String key : langKeys){ msg.append(getLabel(key)); msg.append("<br/>"); }
         * return msg.toString(); }
         */
        return null;
    }

    /*** 根据key获取语言 value *****/
    public String getLang(String key) {
        LoginUser user;
        String lang = null;
        try {
            user = loginUserCtx.getCurrentUser();
            lang = user.getLang();
        } catch (Exception e) {
        }
        if (StringUtils.isEmpty(lang)) {
            lang = SysConfigParams.lang;
        }
        LanguageMap map = languageMap.get(lang);
        if (map == null) {
            return "";
        }
        return map.getLangByKey(key);
    }

    /**
     * 
     * 返回 @Description: TODO @param code 0表示成功，1表示失败 @param message 提示信息 @param data 附带的数据 @param strConvert
     * 是否需要客户端JSON转换 @return AjaxResult @throws
     */
    public AjaxResult returnData(int code, String message, Object data, boolean strConvert) {
        AjaxResult result = new AjaxResult(code, message, data, strConvert);
        return result;
    }

    /**
     * 
     * 成功返回 @Description: TODO @param message 信息 @param data 数据 @param strConvert 是否需要客户端JSON转换 @return
     * AjaxResult @throws
     */
    public AjaxResult returnSuccData(String message, Object data, boolean strConvert) {
        return this.returnData(0, message, data, strConvert);
    }

    /**
     * 
     * 成功返回 @Description: TODO @param message 信息 @param data 数据 @return AjaxResult @throws
     */
    public AjaxResult returnSuccData(String message, Object data) {
        return this.returnSuccData(message, data, false);
    }

    /**
     * 
     * 成功返回
     * 
     * @param data
     *            数据
     * @return AjaxResult @throws
     */
    public AjaxResult returnSuccData(Object data) {
        return this.returnSuccData(getLang("common_operatorSuccess"), data, false);
    }

    /**
     * 
     * 成功返回 @Description: TODO @param message 信息 @param data 数据 @return AjaxResult @throws
     */
    public AjaxResult returnSuccess() {
        return this.returnSuccData(getLang("common_operatorSuccess"), null);
    }

    /**
     * 
     * 成功返回 @Description: TODO @param message 信息 @param data 数据 @return AjaxResult @throws
     */
    public AjaxResult returnSuccData(String message) {
        return this.returnSuccData(message, null);
    }

    /**
     * 返回失败结果 @Description: TODO @param message 信息 @return AjaxResult @throws
     */
    public AjaxResult returnFailData(String message) {
        return this.returnFailData(message, null, false);
    }

    /**
     * 返回失败结果 @Description: TODO @param message 信息 @param data 数据 @return AjaxResult @throws
     */
    public AjaxResult returnFailData(String message, Object data) {
        return this.returnFailData(message, data, false);
    }

    /**
     * 返回失败结果 @Description: TODO @param message 信息 @param data 数据 @param strConvert 是否需要客户端json转换 @return
     * AjaxResult @throws
     */
    public AjaxResult returnFailData(String message, Object data, boolean strConvert) {
        return this.returnData(1, message, data, strConvert);
    }

    /**
     * @Title: saveFilter @Description: 新增/更新保存前的过滤操作 ,在此嵌入验证 @param bean 实体bean @param isupdate 是更新、或者新增 @return
     *         返回空字符串信息则执行保存，否则不执行保存，并且返回信息发给客户端 @throws
     */
    public String saveFilter(T bean, boolean isupdate, HttpServletRequest request) {
        HashSet<String> excFields = new HashSet<String>();
        if (isupdate) {
            // 更新情况下，需要检查是否存在新旧值校验
            Field[] fields = entityClass.getDeclaredFields();
            for (Field f : fields) {
                String pName = f.getName();
                NewOldCompare noCompare = f.getAnnotation(NewOldCompare.class);
                if (noCompare != null) {
                    String newValue = request.getParameter(pName);
                    String tmpName = "old_" + pName;
                    String oldValue = request.getParameter(tmpName);
                    if (oldValue == null) {// 读取数据库检查字段值是否已经修改
                        K beanId = null;
                        try {
                            if (getIdMethod == null) {
                                getIdMethod = entityClass.getMethod("getId");
                                getIdMethod.setAccessible(true);
                            }
                            beanId = (K)(getIdMethod.invoke(bean));
                        } catch (Exception e) {
                            return pName + getLang("common_newOldCompare_excption");
                        }
                        if (beanId != null) {
                            try {
                                T dbBean = this.service.get(beanId);
                                String filedName = pName.substring(0, 1).toUpperCase() + pName.substring(1);
                                Method getMethod = entityClass.getMethod("get" + filedName);
                                getMethod.setAccessible(true);
                                oldValue = String.valueOf(getMethod.invoke(dbBean));
                            } catch (Exception e) {
                                return pName + getLang("common_newOldCompare_excption");
                            }
                        }
                    }
                    if (oldValue.equals(newValue)) {// 新旧相等则排除验证
                        excFields.add(pName);
                    }
                }
            }
        }
        return AdminControllerHelper.validEntityBean(bean, excFields, this, request);
    }

    /**
     * @Title: getLoginUser @Description: 从当前请求中获取登陆用户的数据 @param request @return @throws
     */
    protected LoginUser getLoginUser() {
        try {
            LoginUser u = loginUserCtx.getCurrentUser();
            return u;
        } catch (Exception e) {
            logger.error("{}", e);
            return null;
        }
    }

    /**
     * @Title: 设置更新操作成功后，返回json中的data字段数据 @Description: 子类重写该方法，读取更新后的数据放到更新操作结果里以便客户端使用更新后的数据 @return 默认返回null @throws
     */
    protected Object setAnUResult(T t) throws Exception {
        return JSONObject.toJSON(t);
    }

    /***
     * 更新情况下设置实体的json到request的entityJson中
     ***/
    protected void setUpdateObjectJson(HttpServletRequest request) throws Exception {
        String id = request.getParameter("id");
        if (StringUtils.isNotEmpty(id)) {
            @SuppressWarnings("unchecked")
            K k = (K)ConvertUtils.convert(id, idClass);
            T obj = service.get(k);
            String json =
                JSONObject.toJSONString(obj, setJsonDateSerialize(), SerializerFeature.DisableCircularReferenceDetect);
            request.setAttribute("entityJson", json);
        } else {
            id = "";
            request.setAttribute("entityJson", "{}");
        }
        request.setAttribute("id", id);
    }

    protected SerializeConfig setJsonDateSerialize() {
        return dateTimeSerialize;
    }

    /**
     * 带时间格式的json转换
     **/
    protected String toJSONStringByDateFormate(Object bean) {
        String json =
            JSONObject.toJSONString(bean, setJsonDateSerialize(), SerializerFeature.DisableCircularReferenceDetect);
        return json;
    }

    /***
     * 可以重写从子类本身的静态变量取值
     ***/
    public DataGrid getDataGridCfg(HttpServletRequest request) {
        DataGrid dg = null;
        String gridid = request.getParameter("gridid");
        if (gridid == null) {
            Object _gridid = request.getAttribute("gridid");
            if (_gridid != null) {
                gridid = String.valueOf(_gridid);
            }
        }
        String filePreName = getDataGridFilePreName(request);
        if (gridid != null) {
            try {
                try {
                    dg = DataGridHelper.getDataGridCfg(setGridModelPath(request), filePreName, gridid);
                } catch (Throwable e) {
                    logger.error("getDataGridCfg:" + e);
                }
            } catch (Throwable e) {
                logger.error("getDataGridCfg:" + e);
            }
        }
        return dg;
    }

    /**
     * 设置datagrid配置的模块路径，默认为configHome/datagrid/base
     ***/
    public String setGridModelPath(HttpServletRequest request) {
        return "base/";
    }

    /**
     * 获取datagrid配置文件的前缀名称，默认返回null则以gridid参数为文件前缀
     ***/
    protected String getDataGridFilePreName(HttpServletRequest request) {
        return null;
    }

    /**
     * 设置页面的模块路径，不设置的情况下，默认为WEB-INF/pages/路径下的实体bean名称
     ***/
    public String setPageModelPath(HttpServletRequest request) {
        return entityPath; // 实体bean名称作为模块文件夹名称
    }

    @Override
    public boolean serviceValid(Map<String, Object> params) throws Exception {
        return service.uniqueValid(params);
    }

    @Override
    public void extGridResult(T bean, JSONObject jsonObj) throws Exception {
        // TODO Auto-generated method stub

    }

    /**
     * 数据权限，可以根据数据bean来控制按钮的权限 默认走 hasPrivilage(request,cmd)
     ***/
    @Override
    public boolean hasDataPrivilage(T bean, HttpServletRequest request, String cmd) {
        return hasPrivilage(request, cmd);
    }

    /**
     * 按钮权限UI控制逻辑
     ***/
    @Override
    public boolean hasPrivilage(HttpServletRequest request, String cmd) {
        LoginUser user = getLoginUser();
        if (user == null) {
            return false;
        }
        if (user.isSuperAdmin()) {
            return true;
        }
        boolean pri = false;
        Map<String, List<UserFunc>> authMap = user.getAuthMap();
        UrlInfo info = AdminControllerHelper.getControllerAndCmd(request);
        String authKey = info.getAuthController();
        if (StringUtils.isEmpty(cmd)) {
            cmd = info.getCmd();
        }
        List<UserFunc> cmdList = authMap.get(authKey);
        for (UserFunc f : cmdList) {
            if (f.getFuncCode().equals(cmd)) {
                pri = true;
                break;
            }
        }
        return pri;
    }

    @Override
    public void buttonCreateHanlder(T bean, ClientButton button) {
        // TODO Auto-generated method stub

    }

    /**
     * 获取服务service bean
     ***/
    protected Object getBean(String beanName) {
        try {
            return SpringContextUtil.getBean(beanName);
        } catch (Exception ex) {
            logger.error("{}", ex);
        }
        return null;
    }

    /**
     * @throws Throwable
     *             用于列表转树形 需要子类，重写实现 createTreeNode 、getTreeNodeId、getTreeNodeText getTreeNodePid isTreeNodeLeaf
     *             createTreeNodeButton @param list @return @throws
     */
    @SuppressWarnings("unchecked")
    protected <E> JSONArray listToTreeJson(List<E> list, String pid) throws Throwable {
        return this.listToTreeJson(list, pid, null);
    }

    /**
     * @throws Throwable
     *             用于列表转树形 需要子类，重写实现 createTreeNode 、getTreeNodeId、getTreeNodeText getTreeNodePid isTreeNodeLeaf
     *             createTreeNodeButton @param list @param extParams 转换过程总需要用到的参数 @return @throws
     */
    @SuppressWarnings("unchecked")
    protected <E> JSONArray listToTreeJson(List<E> list, String pid, Object extParams) throws Throwable {
        final ResetfulBaseController<T, K> controller = this;
        ListToTree<E> toTree = new ListToTree<E>(new ArrayList<ClientButton>(), new IListToTree() {
            @Override
            public void createNode(Object bean, JSONObject obj, Object extParams) {
                controller.createTreeNode(bean, obj, extParams);
            }

            @Override
            public String getTreeId(Object bean, Object extParams) {
                return controller.getTreeNodeId(bean, extParams);
            }

            @Override
            public String getTreeText(Object bean, Object extParams) {
                return controller.getTreeNodeText(bean, extParams);
            }

            @Override
            public String getTreePid(Object bean, Object extParams) {

                return controller.getTreeNodePid(bean, extParams);
            }

            @Override
            public boolean isTreeLeaf(Object bean, Object extParams) {
                return controller.isTreeNodeLeaf(bean, extParams);
            }

            @Override
            public ClientButton createTreeButton(ClientButton btn, Object bean, Object extParams) {
                return controller.createTreeNodeButton(btn, bean, extParams);
            }
        });
        return toTree.getJSONArray(pid, list, extParams);
    }

    /**
     * 调用listToTreeJson时候可以重写该方法实在对tree节点JSONObject的处理
     */
    public void createTreeNode(Object bean, JSONObject obj, Object extParams) {

    }

    /**
     * 调用listToTreeJson时候可以重写该方法返回树节点的id
     */
    public String getTreeNodeId(Object bean, Object extParams) {
        return null;
    }

    /**
     * 调用listToTreeJson时候可以重写该方法返回树节点的text文本
     */
    public String getTreeNodeText(Object bean, Object extParams) {
        return null;
    }

    /**
     * 调用listToTreeJson时候可以重写该方法返回树节点的pid
     */
    public String getTreeNodePid(Object bean, Object extParams) {
        return null;
    }

    /**
     * 调用listToTreeJson时候可以重写该方法判断树节点是否是子页节点
     */
    public boolean isTreeNodeLeaf(Object bean, Object extParams) {
        return true;
    }

    /**
     * 调用listToTreeJson时候可以重写该方法返回树节点上需要创建的按钮
     */
    public ClientButton createTreeNodeButton(ClientButton btn, Object bean, Object extParams) {
        return null;
    }

}
